﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Controls;

namespace Cloak.Mvvm.Navigation
{
	/// <summary>
	/// A map of page types to view models using attributes
	/// </summary>
	public sealed class AttributedPageModelMap : IPageModelMap
	{
		#region IPageModelMap
		/// <summary>
		/// Gets a reference to the view model associated with the specified page type via an instance of <see cref="ViewModelAttribute"/>
		/// </summary>
		/// <param name="pageType">The type of page to map to a view model</param>
		/// <returns>A reference to the view model mapped to the page type, or null if the page type is not associated with a view model</returns>
		public PageModelReference GetPageModelReference(Type pageType)
		{
			var viewModelAttribute = GetViewModelAttribute(pageType);

			return viewModelAttribute == null ? null : GetPageModelReference(pageType, viewModelAttribute);
		}
		#endregion

		private static ViewModelAttribute GetViewModelAttribute(Type pageType)
		{
			return pageType.GetCustomAttributes(typeof(ViewModelAttribute), true).Cast<ViewModelAttribute>().SingleOrDefault();
		}

		private PageModelReference GetPageModelReference(Type pageType, ViewModelAttribute viewModelAttribute)
		{
			var parameters = CreateParameters(pageType);

			return viewModelAttribute.Name != null
				? new NamedPageModelReference(viewModelAttribute.Type, viewModelAttribute.Name, parameters)
				: new PageModelReference(viewModelAttribute.Type, parameters);
		}

		private static IEnumerable<PageModelParameter> CreateParameters(Type pageType)
		{
			return pageType
				.GetCustomAttributes(typeof(ViewModelQueryStringParameterAttribute), true)
				.Cast<ViewModelQueryStringParameterAttribute>()
				.Select(CreateParameter);
		}

		private static PageModelParameter CreateParameter(ViewModelQueryStringParameterAttribute attribute)
		{
			return new PageModelParameter(
				attribute.ViewModelParameterName ?? attribute.QueryStringKey,
				attribute.ValueType ?? typeof(string),
				attribute.QueryStringKey);
		}
	}
}